#------------------------------------------------------------------------
# --------------------- Import ------------------------------------------
#------------------------------------------------------------------------

import binascii
import socket
import struct
import time
import traceback
import types
import sys
import sqlalchemy
import commands
import os
from mod_python import psp
from mod_python import apache
from mod_python import util
from sqlalchemy import *
from binascii import hexlify, unhexlify
from M2Crypto import EVP
from M2Crypto import *
from xml.dom.minidom import *
from xml.dom import minidom, Node

try:
    from mod_python import apache
    from mod_python import Cookie
except ImportError:
    # this happens if you use this outside of mod_python, which
    # is okay.
    pass
import md5
import time

#========================================================================
# --------------------- Alias ------------------------------------------
#========================================================================

DEFAULT = "0000"
STATUS = "STATUS"
ERROR = "ERROR"

# Site - Client
RTS = "RTS"  
CTS = "CTS"

DATA = "DATA"
MSG = "MSG"
CMD = "CMD"

FILE = "FILE"
PATH = "PATH"
FILENAME = "FILENAME"


debug = open('/tmp/debug.txt','a')
resDebug = open('/tmp/resultNodes.txt','a')

#========================================================================
# --------------------- Import ------------------------------------------
#========================================================================

def test():
    return "test"


#--------------------------------------------------------------------------
def makesecureserversocket( port, file, backlog=5 ):
#--------------------------------------------------------------------------
	""" Constructs and prepares a secure server socket listening on the given 
	port.

	"""
	ctx = SSL.Context('sslv3')
	certfile= file+".pem"
	keyfile=file+".key"
	ctx.load_cert( certfile , keyfile, call)
	bindsocket = SSL.Connection(ctx)
	bindsocket.bind(('', port))
	bindsocket.listen(backlog)
	#s, fromaddr = bindsocket.accept()

	return bindsocket


#--------------------------------------------------------------------------
def connectandsend ( host, port, msgdata,  pid=None, waitreply=True ):
#--------------------------------------------------------------------------
    """
    connectandsend( host, port, message type, message data, peer id,
    wait for a reply ) -> [ ( reply type, reply data ), ... ]

    Connects and sends a message to the specified host:port. The host's
    reply, if expected, will be returned as a list of tuples.

    """
    msgreply = []
    try:
        peerconn = BTPeerSecureConnection( pid, host, port, debug=True )
        peerconn.senddata( msgdata )

        if waitreply:
            onereply, msgtype = peerconn.recvdata()

        while (onereply !=  None ):
            msgreply.append( onereply )
            onereply, msgtype = peerconn.recvdata()
        peerconn.close()
    except:
        raise

    return msgreply

    # end connectsend method


class BTPeerSecureConnection:

    #--------------------------------------------------------------------------
    def __init__( self, peerid, host, port, sock=None, debug=True):
    #--------------------------------------------------------------------------
    # any exceptions thrown upwards

        self.debug = debug
        print >> resDebug , "Starting Connection with %s:%s" % (str(host), str(port))
        #self.s = None 
        self.id = peerid
        self.s = None
        try:
            if not sock:

                ctx = SSL.Context('sslv3')

                self.s = SSL.Connection(ctx)
                #self.s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
                self.s.connect(( host, port ))

            else:
                self.s = sock

            self.sd = self.s.makefile( 'rw', 0 )

        except:
            raise

    #--------------------------------------------------------------------------
    def __makemsg( self, msgtype, msgdata ):
    #--------------------------------------------------------------------------
        msglen = len(msgdata)
        msg = struct.pack( "!4sL%ds" % msglen, msgtype, msglen, msgdata )
        return msg

    #--------------------------------------------------------------------------
    def clientInfo( self ):
    #--------------------------------------------------------------------------    
        return (self.s.getpeername())

    #--------------------------------------------------------------------------
    def clientSocket( self ):
    #--------------------------------------------------------------------------    
        return (self.s)


    #--------------------------------------------------------------------------
    def __debug( self, msg ):
    #--------------------------------------------------------------------------
        if self.debug:
            btdebug( msg )


    #--------------------------------------------------------------------------
    def senddata( self,  msgdata , msgtype="NONE" ):
    #--------------------------------------------------------------------------
        """
        senddata( message type, message data ) -> boolean status
    
        Send a message through a peer connection. Returns True on success
        or False if there was an error.
        """
    
        try:
            msg = self.__makemsg( msgtype, msgdata )
            self.sd.write( msg )
            self.sd.flush()
        except KeyboardInterrupt:
            raise "KeyboardInterrupt"
        except:
            traceback.print_exc()
            return False
        return True


    #--------------------------------------------------------------------------
    def recvdata( self ):
    #--------------------------------------------------------------------------
        """
        recvdata() -> (msgtype, msgdata)
    
        Receive a message from a peer connection. Returns (None, None)
        if there was any error.
        """
    
        try:
            msgtype = self.sd.read( 4 )
            #if not msgtype: return (None, None)
                
            lenstr = self.sd.read( 4 )
            msglen = int(struct.unpack( "!L", lenstr )[0])
            msg = ""
            while len(msg) != msglen:
                data = self.sd.read( min(2048, msglen - len(msg)) )
                if not len(data):
                    break
                msg += data
    
            if len(msg) != msglen:
                return None, None
    
        except KeyboardInterrupt:
            raise
        except:
            if self.debug:
                traceback.print_exc()
            return None, None
    
        return  msg , msgtype
    
        # end recvdata method


    #--------------------------------------------------------------------------
    def close( self ):
    #--------------------------------------------------------------------------
        """
        close()
    
        Close the peer connection. The send and recv methods will not work
        after this call.
        """
    
        self.s.close()
        self.s = None
        self.sd = None


    #--------------------------------------------------------------------------
    def __str__( self ):
    #--------------------------------------------------------------------------
        return "|%s|" % peerid



#========================================================================
#---------------------- XML PART ----------------------------------------
#========================================================================

#--------------------------------------------------------------------------
def extractData ( msgdata ) :
#--------------------------------------------------------------------------
    
    try :
        #raise Exception(msgdata)
        msgdata = msgdata[0]
        doc = minidom.parseString( msgdata )
        data = doc.documentElement
        data = (data.getElementsByTagName("Packet"))[0]
        typeMsg = str( data.getAttribute("Type") )

        pkt = str( data.getAttribute("Value") )

        return pkt

    except :
        raise 
        raise Exception("Debug  msg : Could not extract data properly")



#--------------------------------------------------------------------------
def createXMLcontent( masterIp , masterPort , slaveIp, slavePort,  msgType ,content , xmlType=DATA):
#--------------------------------------------------------------------------

    # Creating XML to send
    doc = Document()

    dataElem = doc.createElement( xmlType )
    
    doc.appendChild( dataElem )

    masterElem = doc.createElement( "MasterHost" )
    masterElem.setAttribute( "Ip" , masterIp)
    masterElem.setAttribute( "Port" , str(masterPort) )
    dataElem.appendChild( masterElem )

    slaveElem = doc.createElement( "SlaveHost" )
    slaveElem.setAttribute( "Ip" , slaveIp)
    slaveElem.setAttribute( "Port" , str(slavePort))
    dataElem.appendChild( slaveElem )

    msgElem = doc.createElement( "Packet" )
    msgElem.setAttribute( "Value" , content)
    msgElem.setAttribute( "Type" , msgType )
    dataElem.appendChild( msgElem )

    sendingDoc = doc.toprettyxml(indent=" ")

    print ( "Sending" , sendingDoc )
    return sendingDoc



#========================================================================
#---------------------- Sending Function ----------------------------------------
#========================================================================


#--------------------------------------------------------------------------
def sendBetweenNode(masterHost=(None,None) , slaveHost=(None,None) , action=None, args=None ) :
#--------------------------------------------------------------------------
# zhtaei apo ton Master node na ektelesei mia entolh ston Slave node

    masterIp = masterHost[0]
    masterPort = masterHost[1]

    #raise Exception(slaveHost)
    if slaveHost == None :
        return "Connection Not Possible"

    slaveIp = slaveHost[0]
    slavePort = slaveHost[1]

    # Asking slave node to allow Master to send
    openPortReq = createXMLcontent( masterIp , masterPort , slaveIp , slavePort, "" , "", RTS)

    try :
        allow = connectandsend( slaveIp, slavePort, openPortReq , True )
        allowed = extractData ( allow )
    except :
        #return "Could not connect"
        raise Exception(slaveIp , slavePort , slaveHost)

    print >> resDebug , allowed
    if allowed == "TRUE" :
        print >> resDebug ,  "Ready to connect"
    else :
        print >> resDebug ,  "Slave failed to open ports"
        return allowed
        #return "Slave Could not open ports or Failed extracting Data"

    print >> resDebug , "Action ", str(action)
    toBeSent = ""

    if action == "LS" :
        command = "ls" 
        toBeSent = createXMLcontent( masterIp , masterPort , slaveIp , slavePort, CMD , command)

    elif action == "Rename" :
        command = "mv" + args
        toBeSent = createXMLcontent( masterIp , masterPort , slaveIp , slavePort, CMD , command)

    elif action == "Newdir" :
        command  = "mkdir" + args
        toBeSent = createXMLcontent( masterIp , masterPort , slaveIp , slavePort, CMD , command)

    elif action == "Newfile" :
        command =  "touch" + args
        toBeSent = createXMLcontent( masterIp , masterPort , slaveIp , slavePort, CMD , command)

    elif action == "Copy" :
        command =  "cp" + args
        toBeSent = createXMLcontent( masterIp , masterPort , slaveIp , slavePort, CMD , command)

    elif action == "Delete" :
        command =  "rm" + args
        toBeSent = createXMLcontent( masterIp , masterPort , slaveIp , slavePort, CMD , command)
    elif action == "Chmod" :
        splitted = args.split(' ')
        command = "chmod " + splitted[2] + ' ' +  splitted[1]
        toBeSent = createXMLcontent( masterIp , masterPort , slaveIp , slavePort, CMD , command)

    elif action == "Size" :
        command =  "du" + args
        toBeSent = createXMLcontent( masterIp , masterPort , slaveIp , slavePort, CMD , command)

    elif action == "Transfer" :
        path = args
        toBeSent = createXMLcontent( masterIp , masterPort , slaveIp , slavePort, PATH , path , FILE )

    elif action == "STATUS" :
        toBeSent = createXMLcontent( masterIp, masterPort , slaveIp, slavePort, "request", "status", STATUS)
      #  print >> resDebug , "LALA %s " % toBeSent

    else :
        print "No such choice. Return to main menu"
        return "No Such Choice : ", str(action)


    print >> debug, "\n------------- Critical Part --------------\n"
# TODO : kapws prepei na exw to onoma tou server sthn morfh (ip, port). twra me to xeri
    if toBeSent != False :
        try :
            if (masterIp, masterPort) != (str("139.91.70.38"), 11224) :
                print >> resDebug, "Sending through intermediate\n"
                replyXML = connectandsend( masterIp, masterPort, toBeSent , True )
            else :
                print >> resDebug, "Sending directly\n"
                replyXML = connectandsend( slaveIp, slavePort, toBeSent , True )			

                reply = extractData ( replyXML )
                print >> resDebug, "Replied ", reply , " \n--------Critical Is Over ---------\n"
                return "Answer " + str(reply)
        except :
            raise
    else :
        print "Failed to send command\n -------- Critical Is Over ------------\n"
        return "Failed to communicate"




